using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using MetaObjects.Contracts;
using MetaObjects.Core;
using Microsoft.SharePoint;

namespace MetaObjects.SharePoint
{
	public class SharePointFieldProvider : MetaFieldProvider<SPField>
	{
		public Dictionary<Type, Dictionary<Type, IMetaFieldProvider>> Cache { get; private set; }
		static readonly Type[] ProviderTypes;
		private static Dictionary<Type, IMetaFieldProvider> TypeCache;

		static SharePointFieldProvider()
		{
			Assembly assembly = Assembly.GetExecutingAssembly();
			ProviderTypes = assembly.GetExportedTypes().Where(t => typeof(IMetaFieldProvider).IsAssignableFrom(t) && !t.IsAbstract).ToArray();
		}

		public SharePointFieldProvider()
		{
			TypeCache = new Dictionary<Type, IMetaFieldProvider>();
			Cache = new Dictionary<Type, Dictionary<Type, IMetaFieldProvider>>();
			var textFieldCache = new Dictionary<Type, IMetaFieldProvider>();
			textFieldCache.Add(typeof(string), new FieldText());
			Cache.Add(typeof(SPFieldText), textFieldCache);
		}

		protected override IMetaField GetMetaField(SPField field)
		{
			IMetaFieldProvider provider = Resolve(field.GetType(), field.FieldValueType ?? typeof(object));
			if (provider == null)
			{
				return new SharePointField(field.InternalName)
		       	{
		       		DisplayName = field.Title,
		       		Description = field.Description,
		       		Required = field.Required
		       	};
			}
			return provider.FieldFactory(field);
		}

		private static IMetaFieldProvider Resolve(Type fieldType, Type valueType)
		{
			if (valueType.IsValueType)
			{
				valueType = typeof (Nullable<>).MakeGenericType(valueType);
			}
			Type type = typeof (FieldBase<,>).MakeGenericType(fieldType, valueType);
			if (TypeCache.ContainsKey(type))
			{
				return TypeCache[type];
			}
			Type providerType = ProviderTypes.Where(type.IsAssignableFrom).FirstOrDefault();
			if (providerType == null)
			{
				return null;
			}
			IMetaFieldProvider provider = (IMetaFieldProvider) Activator.CreateInstance(providerType);
			TypeCache.Add(type, provider);
			return provider;
		}
	}
}